package com.lam.framework.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.filter.CorsFilter;

import com.lam.common.base.domain.MsgCode;
import com.lam.common.base.domain.Result;
import com.lam.common.utils.ServletUtils;
import com.lam.framework.security.filter.AuthenticationTokenFilter;
import com.lam.framework.security.handler.LamAuthenticationEntryPoint;
import com.lam.framework.security.handler.LamLogoutSuccessHandler;
import com.lam.framework.security.provider.LamAuthenticationProvider;

/**
 * spring security配置
 * 
 * @author lam
 */
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	
    /** 认证失败处理类 */
    @Autowired
    private LamAuthenticationEntryPoint unauthorizedHandler;
    /** 退出处理类 */
    @Autowired
    private LamLogoutSuccessHandler logoutSuccessHandler;
    /** token认证过滤器 */
    @Autowired
    private AuthenticationTokenFilter authenticationTokenFilter;
    /** 跨域过滤器 */
    @Autowired
    private CorsFilter corsFilter;
    @Autowired
    private LamAuthenticationProvider lamAuthenticationProvider;
    
    /**
     * 解决 无法直接注入 AuthenticationManager
     * @return
     * @throws Exception
     */
	@Bean
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}
	
	/**
	 * 身份认证接口
	 */
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.authenticationProvider(lamAuthenticationProvider);		//自定义密码验证规则
	}

    /**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问（非remember-me下自动登录）
     * hasAnyAuthority     |   如果有参数，参数表示权限，则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数，参数表示角色，则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数，参数表示权限，则其权限可以访问
     * hasIpAddress        |   如果有参数，参数表示IP地址，如果用户IP和参数匹配，则可以访问
     * hasRole             |   如果有参数，参数表示角色，则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
    	// CRSF禁用，因为不使用session
        httpSecurity.csrf().disable() 
	        // 认证失败处理类
	        .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
	        // 基于token，所以不需要session
	        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
		        // 过滤请求
		        .authorizeRequests()
		        
		        // 后台登录login 验证码captchaImage 允许匿名访问
		        .antMatchers("/adm/index/login", "/adm/captcha/getOpen", "/adm/captcha/code", "/adm/captcha/pic", "/adm/captcha/pic/check").anonymous()
		        
		        // 前端登录login注册 验证码captchaImage 必须匿名访问
		        .antMatchers("/api/login/loginIn", "/api/login/reg", "/api/register", "/api/login").anonymous()
		        // 前端登录login注册 验证码captchaImage 允许匿名访问
		        .antMatchers("/api/login/loginOut", "/api/indexImgs", "/api/prod/tag/prodTagList",
		        		"/api/notice/topNoticeList", "/api/prod/lastedProdPage", "/api/category/categoryInfo",
		        		"/api/search/hotSearchByShopId", "/api/search/searchProdPage", "/api/prod/pageProd", 
		        		"/api/prod/prodListByTagId", "/api/prod/prodInfo", "/api/prodComm/prodCommData",
		        		"/api/prodComm/prodCommPageByProd", "/api/user/collection/isCollection").permitAll()
		        
		        .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/api/common/img").permitAll()
		        
		        //对actuator监控接口进行权限设置
		        .antMatchers("/adm/actuator/metrics/jvm.**").access("@ss.hasPm('monitor:actuator:jvm')")
		        .antMatchers("/adm/actuator/metrics/tomcat.**").access("@ss.hasPm('monitor:actuator:tomcat')")
		        .antMatchers("/adm/actuator/metrics/process.**").access("@ss.hasPm('monitor:actuator:process')")
		        .antMatchers("/adm/actuator/metrics/system.**").access("@ss.hasPm('monitor:actuator:system')")
		        .antMatchers("/adm/actuator/metrics/**").access("@ss.hasPm('monitor:actuator:metricsAll')")
		        .antMatchers("/adm/actuator/**").access("@ss.hasPm('monitor:actuator:all')")
		        
//		        .antMatchers("/druid/**").permitAll()
		        // 除上面外的所有请求全部需要鉴权认证
		        .anyRequest().authenticated()
		        .and()
	        .headers().frameOptions().disable();
        
        // 没有登录
        httpSecurity.exceptionHandling().authenticationEntryPoint((request, response, e)->
        		ServletUtils.outWrite(response, Result.fail("您还未登录，请先登录系统！").code(MsgCode.UNAUTHORIZED.getCode())));
        
        // 权限不足
//        httpSecurity.exceptionHandling().accessDeniedHandler((request, response, e)->
//        		ServletUtils.outWrite(response, Result.fail("没有权限，请联系管理员授权").code(MsgCode.FORBIDDEN.getCode())));
        
        httpSecurity.logout().logoutUrl("/adm/index/logout").logoutSuccessHandler(logoutSuccessHandler);
        // 添加Auth filter
        httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        // 添加CORS filter
        httpSecurity.addFilterBefore(corsFilter, AuthenticationTokenFilter.class);
        httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
    }
    
}
